package controller

import (
	"context"
	"fmt"
	"math/rand"
	"reflect"
	"time"

	"gddgame.cc/galaxy/asteroid"
	"gddgame.cc/galaxy/asteroid/cluster"
	. "gddgame.cc/galaxy/common/dsl/game/task"
	. "gddgame.cc/galaxy/common/dsl/web"
	"gddgame.cc/galaxy/project/star/star/model"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/def"
	"gddgame.cc/galaxy/utils/serialize"
)

const (
	WarEvent = "zone"
)

type WarNode struct {
	times   int
	success int
	reduce  int
	percent float32
	power   float32
}

type WarStrategy struct {
	def.Context
	success chan *model.ZoneNode
	fail    chan *model.ZoneNode
	nodes   map[string]*WarNode
}

func (s *WarStrategy) Interval() time.Duration {
	return 60 * time.Minute
}

func ZoneNextTime() time.Duration {
	now := time.Now()
	return time.Minute*time.Duration(60-now.Minute()) + time.Second*time.Duration(60-now.Second())
}

func (s *WarStrategy) Listen(context context.Context) error {
	go func() {
		for {
			after := time.NewTimer(ZoneNextTime())
			select {
			case <-after.C:
				// 按间隔时间调度
				s.UpdatePower()
				Center.TaskData, _ = GetTaskData(Env, nil, reflect.TypeOf(&model.TaskInside{}), Center.ItemData)
			case node := <-s.success:
				after.Stop()
				s.Battle(node, true)
			case node := <-s.fail:
				after.Stop()
				s.Battle(node, false)
			case <-context.Done():
				after.Stop()
				return
			}
		}
	}()
	return nil
}
func (s *WarStrategy) UpdatePower() {
	maxScore := model.NodeLevelNumber[len(model.NodeLevelNumber)-1]
	// 计算当前周期的数据
	times := 0
	now := time.Now()

	levelMap := map[int]int{}
	for key := range model.NodeNumberLimit {
		levelMap[key] = 0
	}
	for key := range s.nodes {
		times += s.nodes[key].times
	}
	// 计算单个节点统计信息
	for key, info := range s.nodes {
		info.power = float32(info.times) / float32(times) * 100
		info.percent = float32(info.success) / float32(info.times) * 100
		info.reduce = s.GapReduceScore(warZoneNodeMap[key], info)
	}
	max := s.MaxReduceScore()
	// 统计当前节点的等级分布
	for _, node := range warZoneNodeMap {
		levelMap[node.Level] += 1
	}

	for key, node := range warZoneNodeMap {
		if !node.Status {
			continue
		}
		info, ok := s.nodes[key]
		if !ok {
			info = &WarNode{times: 0, success: 0, reduce: 0, percent: 0, power: 0}
		}
		node.Percent = int(info.percent)
		node.Power = int(info.power)
		// 正常增加积分
		add := s.GapAddScore(node, info)
		// 可减少积分
		reduce := info.reduce
		if reduce > max {
			reduce = max
		}
		// 更新积分
		node.Score = node.Score + add - reduce
		if node.Score >= maxScore {
			node.Score = maxScore - 1
		}

		// 判断节点数量，限制积分
		level := node.ComputeLevel()
		if level != node.Level {
			if levelMap[level] >= model.NodeNumberLimit[level] {
				node.Score = model.NodeLevelNumber[node.Level] - 1
			}
			levelMap[node.Level] -= 1
			node.Level = node.ComputeLevel()
			levelMap[node.Level] += 1
		}
		// 强制更新最后更新时间
		node.UpdateTime = &now
		// 随机任务
		node.Mode, node.MainTask, node.AdditionTask = s.RandomTask(node)
		// 确定通关奖励
		//gifts := map[string]int{}
		//g := model.GiftZoneLogic[node.Level-1][rand.Int31n(int32(len(model.GiftZoneLogic[node.Level-1])))]
		//for key, value := range g {
		//	if value <= 0 {
		//		continue
		//	}
		//	gifts[string(key)] = int(value)
		//}
		//node.Reward = model.Reward{}
		//node.Reward.SetGifts(gifts)
		// 保存
		if _, err := model.ZoneNodeModel.PK(key).Save(false, node); err != nil {
			fmt.Println(err)
		}
	}
	// 重置数据
	s.nodes = make(map[string]*WarNode)
	// 发送更新站点信息
	warClient.All().Send("reload/zone")
}
func (s *WarStrategy) Battle(node *model.ZoneNode, status bool) {
	fmt.Println(node, status)
	// 处理节点战斗
	info, ok := s.nodes[node.Key]
	if !ok {
		info = &WarNode{}
		s.nodes[node.Key] = info
	}
	info.times += 1
	if status {
		info.success += 1
	}
}

// 计算单个节点需增加的感染分
func (s *WarStrategy) GapAddScore(node *model.ZoneNode, info *WarNode) int {
	// 自身增长感染分+相邻节点提供感染分
	// 周围节点
	relationScore := 0
	for _, key := range node.Relation {
		r, ok := warZoneNodeMap[key]
		if !ok {
			continue
		}
		relationScore += r.Level * r.Level
	}
	// 随着战力参与而减少
	power := float32(100/len(warZoneNodeMap)) - info.power
	if power <= 0 {
		return relationScore
	}
	return int(power*float32(model.NodeLevelNumber[node.Level-1]/10)) + relationScore
}

// 计算单个节点需减少的感染分
func (s *WarStrategy) GapReduceScore(node *model.ZoneNode, info *WarNode) int {
	if info == nil {
		return 0
	}
	// 当前节点战斗力分布 * 当前节点战斗成功率 * 当前感染分
	return int(float32(info.power/100) * float32(info.percent/100) * float32(node.Score))
}

// 获取单次最大减少感染分
func (s *WarStrategy) MaxReduceScore() int {
	// 所有节点减分求和的一半
	all := 0
	for _, node := range s.nodes {
		all += node.reduce
	}
	return all / 2
}

func (s *WarStrategy) RandomTask(node *model.ZoneNode) (model.GameMode, int, int) {
	// 先随机类型
	//mode := model.ZoneModeList[rand.Int31n(int32(len(model.ZoneModeList)))]
	modeMap := Center.ZoneTaskPool[node.Mode]
	totalLevel := len(model.NodeLevelNumber)
	// 根据类型随机任务
	maxLevel := node.Level + 1
	if maxLevel > totalLevel {
		maxLevel = totalLevel
	}
	minLevel := node.Level - 1
	if minLevel < 0 {
		minLevel = 0
	}
	maxTaskDifficult := maxLevel * Center.MaxZoneTaskDifficult / totalLevel
	minTaskDifficult := minLevel * Center.MaxZoneTaskDifficult / totalLevel
	if minTaskDifficult < Center.MinZoneTaskDifficult {
		minTaskDifficult = Center.MinZoneTaskDifficult
	}
	if maxTaskDifficult < minTaskDifficult {
		maxTaskDifficult = minTaskDifficult
	}
	number := 2
	taskType := []TaskType{model.ZoneTask, model.ZoneAdditionTask}
	tasks := make([]int, 0, number)
	//fmt.Println(center.MaxZoneTaskDifficult, maxTaskDifficult, minTaskDifficult, minLevel, maxLevel, node.Level, totalLevel)
	for i := 0; i < number; i++ {
		l := int(rand.Int31n(int32(maxTaskDifficult-minTaskDifficult+1))) + minTaskDifficult
		if l > Center.MaxZoneTaskDifficult {
			l = Center.MaxZoneTaskDifficult
		}
		//fmt.Println(l, int32(len(modeMap[taskType[i]][l])))
		number := int32(len(modeMap[taskType[i]][l]))
		if number == 0 {
			continue
		}
		index := int(rand.Int31n(number))
		task := modeMap[taskType[i]][l][index]
		tasks = append(tasks, task)
	}
	// 添加数据库关系
	tt, err := AddZoneTasks(s.Context, node.Key, tasks, node.Level)
	if err != nil {
		s.Error(err)
		return node.Mode, 0, 0
	}
	return node.Mode, tt[0].Id, tt[1].Id
}

var (
	warStrategy    *WarStrategy
	warClient      *cluster.Client
	warZoneNodeMap map[string]*model.ZoneNode
	warCanFunc     context.CancelFunc
	warZoneTaskIds []int

	// system绑定该事件
	BindZoneEvent = func(message asteroid.Message) {
		nodeId := message.GetTarget()
		node, ok := warZoneNodeMap[nodeId]
		if !ok {
			return
		}
		bag := serialize.GetBag()
		bag.SetList(message.List())
		event := bag.ReadInterface(0)

		// 可以根据event判断由哪个调度器执行
		switch event {
		case "battle":
			if bag.ReadBool(1) {
				warStrategy.success <- node
			} else {
				warStrategy.fail <- node
			}
		}
		bag.Release()
	}
)

func RunZone(context2 def.Context, client *cluster.Client) error {
	strategy := &WarStrategy{
		nodes:   make(map[string]*WarNode),
		success: make(chan *model.ZoneNode, 30),
		fail:    make(chan *model.ZoneNode, 30),
	}
	ctx, canFunc := context.WithCancel(context.Background())
	warCanFunc = canFunc
	warClient = client
	warStrategy = strategy
	// 绑定Context
	warStrategy.Context = context2

	return strategy.Listen(ctx)
}

func LoadZone(context def.Context) error {
	context.Infof("Load Zone")
	var err error

	// Zone
	var nodes []model.ZoneNode
	_, err = model.ZoneNodeModel.Query().Find(&nodes)
	if err != nil {
		return err
	}
	tmpMap := make(map[string]*model.ZoneNode, len(nodes))
	tmpIds := []int{}
	for index := range nodes {
		node := nodes[index]
		node.Init()
		//node.Mode, node.MainTask, node.AdditionTask = warStrategy.RandomTask(&node)
		//os.Exit(0)
		// 预处理任务逻辑
		node.TargetRandom = utils.NewWeightRandom()
		node.BossRandom = utils.NewWeightRandom()
		node.ObjectRandom = utils.NewWeightRandom()
		node.Targets = make([]int, 0)
		node.Boss = make([]int, 0)
		node.Objects = make([]int, 0)
		var taskList []*GameTask
		if node.MainTask != 0 || node.AdditionTask != 0 {
			taskList, err = GetGameTaskList(Env, nil, reflect.TypeOf(&model.TaskInside{}), []int{node.MainTask, node.AdditionTask})
			if err != nil {
				return err
			}
		} else {
			taskList = []*GameTask{}
		}
		for _, task := range taskList {
			flag := true
			for _, id := range tmpIds {
				if id == task.Id {
					flag = false
					break
				}
			}
			if flag {
				tmpIds = append(tmpIds, task.Id)
			}
			if task.Id == node.MainTask {
				node.MainTaskInfo = task
			} else if task.Id == node.AdditionTask {
				node.AdditionTaskInfo = task
			}
			extend := task.Inside.(*model.TaskInside)
			for _, key := range extend.Targets {
				if target, ok := Center.Targets[key]; ok {
					node.Targets = append(node.Targets, target.Id)
					difficult := target.Difficult
					if node.Level > 0 {
						difficult = difficult / node.Level
					}
					node.TargetRandom.Add(target.Id, difficult)
				}
			}
			if extend.Boss > 0 {
				if target, ok := Center.Targets[extend.Boss]; ok {
					node.Boss = append(node.Boss, target.Id)
					difficult := target.Difficult
					if node.Level > 0 {
						difficult = difficult / node.Level
					}
					node.BossRandom.Add(target.Id, difficult)
				}
			}
			for _, key := range extend.Objects {
				if object, ok := Center.ObjectMap[key]; ok {
					node.Objects = append(node.Objects, object.Id)
					difficult := object.Difficult
					if node.Level > 0 {
						difficult = difficult / node.Level
					}
					node.ObjectRandom.Add(object.Id, difficult)
				}
			}
		}

		tmpMap[node.Key] = &node
	}
	warZoneTaskIds = tmpIds
	warZoneNodeMap = tmpMap
	next := time.Now().Add(ZoneNextTime())
	Center.InitZoneLanguage(warZoneNodeMap, &next)
	return nil
}

func CloseZone(context def.Context) error {
	if warCanFunc != nil {
		warCanFunc()
		warCanFunc = nil
	}
	warClient = nil
	return nil
}

func BattleZone(context def.Context, nodeId string, status bool) error {
	warClient.Emit().List("battle", status).TargetId(nodeId).Send(WarEvent)
	return nil
}

func GetAllZoneNode(context def.Context, language string, updateTime *time.Time) ([]*model.ZoneNodeExt, error) {
	if updateTime != nil {
		zones := make([]*model.ZoneNodeExt, 0, 5)
		for _, zone := range Center.Languages[language].ZoneNodes {
			if zone.HasUpdate(updateTime) {
				zones = append(zones, zone)
			}
		}
		return zones, nil
	} else {
		return Center.Languages[language].ZoneNodes, nil
	}
}

func GetAllZoneTaskIds(context def.Context) ([]int, error) {
	return warZoneTaskIds, nil
}

func GetZoneNode(context def.Context, nodeId string) (*model.ZoneNode, error) {
	node, ok := warZoneNodeMap[nodeId]
	if !ok {
		return nil, EmptyExceptionDetail("NODE_EMPTY", nodeId)
	}
	return node, nil
}
